﻿using Leeder.Logger;
using LogisticsAndong.Comm;
using LogisticsAndong.Model;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading;
using TitaniumAS.Opc.Client.Common;
using TitaniumAS.Opc.Client.Da;

namespace LogisticsAndong.Business
{
    /// <summary>
    /// OPC业务逻辑
    /// </summary>
    public class OpcBusiness
    {
        /// <summary>
        /// 日志
        /// </summary>
        public LogOperate LogObj => Log.GetLogger(this.GetType());

        private MaterialDirectionBLL bllMaterialDirection = new MaterialDirectionBLL();
        private SystemDeviceMonitorBLL bllSystemDeviceMonitor = new SystemDeviceMonitorBLL();
        private PlanMonitorBLL bllLogisticsSystemPlanMonitor = new PlanMonitorBLL();

        public delegate void ChangeDelegate();
        public event ChangeDelegate LipChangeEvent;
        public event ChangeDelegate LsdChangeEvent;

        /// <summary>
        /// OPC LIP组处理逻辑
        /// 物料数量扣减和重置，OPC信号为NO时对应物料数量减1，等于0时重置数量
        /// </summary>
        public void LipGroup(OpcTagModel opcTag, int result)
        {
            //把OPC触发点对应的部品装进队列里，以供另外一个线程读取并做相应的运算
            var plans = GolbalVariable.MaterialDirectionBag.Where(x => x.SignalMark == opcTag.SignalMark && x.DataSource == "OPC").ToList();
            if (plans != null)
                plans.ForEach(x =>
                {
                    //计算方法
                    int left_count = x.LeftCount;
                    CalculationHelper.Subtraction(ref left_count, 1);
                    CalculationHelper.CustomOperation(ref left_count, 3, 1, x.OnceTransportCount);
                    x.LeftCount = left_count;
                    //把运算后的部品实体更新到数据库
                    bllMaterialDirection.Update(x);
                    LipChangeEvent();
                    LogObj.Info("顺引顺建/内制品/排序工程剩余台数扣减(" + x.MaterialName + ":" + x.LeftCount + ")");
                });
        }

        /// <summary>
        /// OPC LSD组处理逻辑
        /// 读取信号灯当前状态，更新缓存，然后同步更新软件界面显示
        /// </summary>
        public void LsdGroup(OpcTagModel opcTag, int result)
        {
            //判断触发点是否为绿灯
            var signalLampModels1 = GolbalVariable.SignalLampShowModelBag.Where(x => x.GreenOutAddress == opcTag.SignalMark).ToList();
            if (signalLampModels1 != null)
                signalLampModels1.ForEach(x =>
                {
                    //更新缓存中绿灯的状态
                    x.IsGreen = result;
                });
            //判断触发点是否为红灯
            var signalLampModels2 = GolbalVariable.SignalLampShowModelBag.Where(x => x.RedOutAddress == opcTag.SignalMark).ToList();
            if (signalLampModels2 != null)
                signalLampModels2.ForEach(x =>
                {
                    //更新缓存中红灯的状态
                    x.IsRed = result;
                });
            LsdChangeEvent();
        }

        /// <summary>
        /// OPC KK组处理逻辑
        /// 更新开捆的链数和台数(直接读取OPC数据，不作处理)，并且根据开捆台数完成对关联信号灯状态的改变
        /// 信号灯共有5种状态：0常态(绿灭红灭)；1提示配送(绿闪红灭)；2未在规定时间内配送(绿闪红亮)；3配送状态(绿亮红灭)；4未在规定时间内完成配送(绿亮红亮)；
        /// </summary>
        public void KkGroup(OpcTagModel opcTag, int result)
        {
            //链数/台数更新
            var unpackings = GolbalVariable.UnpackingDirectionModelBag.Where(x => x.SignalMark == opcTag.SignalMark).ToList();
            if (unpackings != null)
                  foreach(UnpackingDirectionShowModel x in unpackings)
                { 
                    x.UnpackingCount = result;

                    //查找是否绑定信号灯，并根据信号灯设置的参数及当前状态判断信号灯的下一步状态
                    var signalLamps = GolbalVariable.SignalLampDirectionBag.Where(s => s.UnpackingId == x.UnpackingId).ToList();
                    foreach (var signalLamp in signalLamps)
                    {
                        if (signalLamp != null)
                        {
                            //查找出开捆台数对应的开捆链数对象，用于单双链判断
                            var unpacking = GolbalVariable.UnpackingDirectionModelBag.Where(u => u.UnpackingName == x.UnpackingName && u.DataType == "链数").FirstOrDefault();
                            //查找出对应的红灯绿灯地址
                            var infoR = GolbalVariable.OpcTagBag.Where(p => p.SignalMark == signalLamp.RedOutputAddress).ToList();
                            var infoG = GolbalVariable.OpcTagBag.Where(p => p.SignalMark == signalLamp.GreenOutputAddress).ToList();
                            var opcGroup = GolbalVariable.OpcDaGroupList.Where(p => p.Name == "LSD").FirstOrDefault();
                            //单双链逻辑判断(0全部，1单数，2双数)
                            if (signalLamp.ChainMark == "1")
                            {
                                //单数链触发
                                //当前链数为双数时不触发
                                if (!Convert.ToBoolean(unpacking.UnpackingCount % 2))
                                {
                                    //返回不做处理
                                    continue;
                                }

                            }
                            else if (signalLamp.ChainMark == "2")
                            {
                                //双数链触发
                                //当前链数为单数时不触发
                                if (Convert.ToBoolean(unpacking.UnpackingCount % 2))
                                {
                                    //返回不做处理
                                    continue;
                                }
                            }
                            //当前台数等于出发台数时触发(提示开始配送)
                            if (signalLamp.TriggerCount == result)
                            {
                                switch (signalLamp.Status)
                                {
                                    case 0:
                                        //0状态(常态)下变成提示配送(1状态)
                                        signalLamp.Status = 1;
                                        //绿灯闪
                                        infoG.ForEach(f =>
                                        {
                                        //判断当前信号灯是否已经处于闪烁状态(已存在线程名为tag_name的线程)，已存在不再新增闪烁线程
                                        Thread thread;
                                            GolbalVariable.SignalLampBlinkThreadDictionary.TryGetValue(f.TagName, out thread);
                                            if (thread == null)
                                            {
                                            //新增闪灯线程
                                            thread = new Thread(delegate () {
                                                    SignalLampBlink(opcGroup, f.TagName);
                                                });
                                                thread.Name = f.TagName;
                                                thread.Start();
                                                GolbalVariable.SignalLampBlinkThreadDictionary.TryAdd(f.TagName, thread);
                                            }
                                        });
                                        //红灯灭
                                        infoR.ForEach(f =>
                                        {
                                            SignalLampControl(opcGroup, f.TagName, 0);
                                        });
                                        break;
                                    case 1:
                                        //1状态(提示配送)下不做处理
                                        //signalLamp.status = 3;
                                        break;
                                    case 2:
                                        //2状态(未在规定时间内配送)下不做处理
                                        //signalLamp.status = 3;
                                        break;
                                    case 3:
                                        //3状态(配送状态)下不做处理
                                        //signalLamp.status = 0;
                                        break;
                                    case 4:
                                        //4状态(未在规定时间内完成配送)下不做处理
                                        //signalLamp.status = 0;
                                        break;
                                }
                            }
                            //当前台数等于延迟出发台数时触发(提示配送延迟)
                            else if (signalLamp.TriggerDelayCount == result)
                            {
                                switch (signalLamp.Status)
                                {
                                    case 0:
                                        //0状态(常态)下变成未在规定时间内配送(2状态)
                                        signalLamp.Status = 2;
                                        //绿灯闪
                                        infoG.ForEach(f =>
                                        {
                                            Thread thread;
                                            GolbalVariable.SignalLampBlinkThreadDictionary.TryGetValue(f.TagName, out thread);
                                            if (thread == null)
                                            {
                                            //新增闪灯线程
                                            thread = new Thread(delegate () {
                                                    SignalLampBlink(opcGroup, f.TagName);
                                                });
                                                thread.Name = f.TagName;
                                                thread.Start();
                                                GolbalVariable.SignalLampBlinkThreadDictionary.TryAdd(f.TagName, thread);
                                            }
                                        });
                                        //红灯亮
                                        infoR.ForEach(f =>
                                        {
                                            SignalLampControl(opcGroup, f.TagName, 1);
                                        });

                                        //记录出发延迟信息
                                        PlanMonitorModel model = new PlanMonitorModel()
                                        {
                                            DelayState = 2,
                                            ProductName = signalLamp.LampName,
                                            AreaType = int.Parse(x.UnpackingId.ToString()),
                                            AreaName = x.UnpackingName,
                                            Description = "出发延迟",
                                            StartTime = DateTime.Now,
                                            EndTime = DateTime.Now,
                                            WorkStartTime = DateTime.Now,
                                            WorkEndTime = DateTime.Now,
                                            CreateTime = DateTime.Now,
                                        };
                                        bllLogisticsSystemPlanMonitor.Add(model);

                                        break;
                                    case 1:
                                        //1状态(提示配送)下变成未在规定时间内配送(2状态)
                                        signalLamp.Status = 2;
                                        //绿灯闪
                                        infoG.ForEach(f =>
                                        {
                                            Thread thread;
                                            GolbalVariable.SignalLampBlinkThreadDictionary.TryGetValue(f.TagName, out thread);
                                            if (thread == null)
                                            {
                                            //新增闪灯线程
                                            thread = new Thread(delegate () {
                                                    SignalLampBlink(opcGroup, f.TagName);
                                                });
                                                thread.Name = f.TagName;
                                                thread.Start();
                                                GolbalVariable.SignalLampBlinkThreadDictionary.TryAdd(f.TagName, thread);
                                            }
                                        });
                                        //红灯亮
                                        infoR.ForEach(f =>
                                        {
                                            SignalLampControl(opcGroup, f.TagName, 1);
                                        });

                                        //记录出发延迟信息
                                        PlanMonitorModel model1 = new PlanMonitorModel()
                                        {
                                            DelayState = 2,
                                            ProductName = signalLamp.LampName,
                                            AreaType = int.Parse(x.UnpackingId.ToString()),
                                            AreaName = x.UnpackingName,
                                            Description = "出发延迟",
                                            StartTime = DateTime.Now,
                                            EndTime = DateTime.Now,
                                            WorkStartTime = DateTime.Now,
                                            WorkEndTime = DateTime.Now,
                                            CreateTime = DateTime.Now,
                                        };
                                        bllLogisticsSystemPlanMonitor.Add(model1);

                                        break;
                                    case 2:
                                        //2状态(未在规定时间内配送)下不做处理
                                        //signalLamp.status = 3;
                                        break;
                                    case 3:
                                        //3状态(配送状态)下不做处理
                                        //signalLamp.status = 0;
                                        break;
                                    case 4:
                                        //4状态(未在规定时间内完成配送)下不做处理
                                        //signalLamp.status = 0;
                                        break;
                                }
                            }
                            //当前台数等于结束延迟台数时触发(提示配送结束延迟)
                            else if (signalLamp.EndDelayCount == result)
                            {
                                switch (signalLamp.Status)
                                {
                                    case 0:
                                        //0状态(常态)下不做处理
                                        //signalLamp.status = 4;
                                        break;
                                    case 1:
                                        //1状态(提示配送)下不做处理
                                        //signalLamp.status = 3;
                                        break;
                                    case 2:
                                        //2状态(未在规定时间内配送)下不做处理
                                        //signalLamp.status = 3;
                                        break;
                                    case 3:
                                        //3状态(配送状态)下变成未在规定时间内完成配送(4状态)
                                        signalLamp.Status = 4;
                                        //绿灯亮
                                        infoG.ForEach(f =>
                                        {
                                            SignalLampControl(opcGroup, f.TagName, 1);
                                        });
                                        //红灯亮
                                        infoR.ForEach(f =>
                                        {
                                            SignalLampControl(opcGroup, f.TagName, 1);
                                        });

                                        //记录出发延迟信息
                                        PlanMonitorModel model = new PlanMonitorModel()
                                        {
                                            DelayState = 4,
                                            ProductName = signalLamp.LampName,
                                            AreaType = int.Parse(x.UnpackingId.ToString()),
                                            AreaName = x.UnpackingName,
                                            Description = "结束延迟",
                                            StartTime = DateTime.Now,
                                            EndTime = DateTime.Now,
                                            WorkStartTime = DateTime.Now,
                                            WorkEndTime = DateTime.Now,
                                            CreateTime = DateTime.Now,
                                        };
                                        bllLogisticsSystemPlanMonitor.Add(model);

                                        break;
                                    case 4:
                                        //4状态(未在规定时间内完成配送)下不做处理
                                        //signalLamp.status = 0;
                                        break;
                                }
                            }
                        }
                    }
                }
        }

        /// <summary>
        /// OPC LSZT组处理逻辑
        /// 通过拉绳信号完成对信号灯的状态控制
        /// 信号灯共有5种状态：0常态(绿灭红灭)；1提示配送(绿闪红灭)；2未在规定时间内配送(绿闪红亮)；3配送状态(绿亮红灭)；4未在规定时间内完成配送(绿亮红亮)；
        /// </summary>
        public void LsztGroup(OpcTagModel opcTag, int result)
        {
            var opcGroup = GolbalVariable.OpcDaGroupList.Where(p => p.Name == "LSD").FirstOrDefault();
            //根据信号灯当前状态判断拉绳后的状态
            var signalLamps = GolbalVariable.SignalLampDirectionBag.Where(x => x.ControlInputAddress == opcTag.SignalMark).ToList();
            signalLamps.ForEach(x =>
            {
                //查找出对应的开捆对象
                var unpacking = GolbalVariable.UnpackingDirectionModelBag.Where(u => u.UnpackingId == x.UnpackingId).FirstOrDefault();
                //查找出对应的红灯绿灯地址
                var infoR = GolbalVariable.OpcTagBag.Where(p => p.SignalMark == x.RedOutputAddress).ToList();
                var infoG = GolbalVariable.OpcTagBag.Where(p => p.SignalMark == x.GreenOutputAddress).ToList();
                switch (x.Status)
                {
                    case 0:
                        //0状态(常态)下拉绳不作处理
                        break;
                    case 1:
                        //1状态(提示配送)下拉绳信号灯变成3状态(配送状态)
                        x.Status = 3;
                        //绿灯亮
                        infoG.ForEach(f =>
                        {
                            SignalLampControl(opcGroup, f.TagName, 1);
                        });
                        //红灯灭
                        infoR.ForEach(f =>
                        {
                            SignalLampControl(opcGroup, f.TagName, 0);
                        });

                        //记录出发延迟信息
                        PlanMonitorModel model = new PlanMonitorModel()
                        {
                            DelayState = 1,
                            ProductName = x.LampName,
                            AreaType = int.Parse(x.UnpackingId.ToString()),
                            AreaName = unpacking.UnpackingName,
                            Description = "正常作业",
                            StartTime = DateTime.Now,
                            EndTime = DateTime.Now,
                            WorkStartTime = DateTime.Now,
                            WorkEndTime = DateTime.Now,
                            CreateTime = DateTime.Now,
                        };
                        bllLogisticsSystemPlanMonitor.Add(model);

                        break;
                    case 2:
                        //2状态(未在规定时间内配送)下拉绳信号灯变成3状态(配送状态)
                        x.Status = 3;
                        //绿灯亮
                        infoG.ForEach(f =>
                        {
                            SignalLampControl(opcGroup, f.TagName, 1);
                        });
                        //红灯灭
                        infoR.ForEach(f =>
                        {
                            SignalLampControl(opcGroup, f.TagName, 0);
                        });

                        //记录出发延迟信息
                        PlanMonitorModel model1 = new PlanMonitorModel()
                        {
                            DelayState = 3,
                            ProductName = x.LampName,
                            AreaType = int.Parse(x.UnpackingId.ToString()),
                            AreaName = unpacking.UnpackingName,
                            Description = "延迟拉绳",
                            StartTime = DateTime.Now,
                            EndTime = DateTime.Now,
                            WorkStartTime = DateTime.Now,
                            WorkEndTime = DateTime.Now,
                            CreateTime = DateTime.Now,
                        };
                        bllLogisticsSystemPlanMonitor.Add(model1);

                        break;
                    case 3:
                        //3状态(配送状态)下拉绳信号灯变成0状态(配送结束)
                        x.Status = 0;
                        //绿灯灭
                        infoG.ForEach(f =>
                        {
                            SignalLampControl(opcGroup, f.TagName, 0);
                        });
                        //红灯灭
                        infoR.ForEach(f =>
                        {
                            SignalLampControl(opcGroup, f.TagName, 0);
                        });
                        break;
                    case 4:
                        //4状态(未在规定时间内完成配送)下拉绳信号灯变成0状态(配送结束)
                        x.Status = 0;
                        //绿灯灭
                        infoG.ForEach(f =>
                        {
                            SignalLampControl(opcGroup, f.TagName, 0);
                        });
                        //红灯灭
                        infoR.ForEach(f =>
                        {
                            SignalLampControl(opcGroup, f.TagName, 0);
                        });
                        break;
                }
            });
        }

        /// <summary>
        /// OPC SBJK组处理逻辑
        /// 监控硬件设备的状态并同步更新缓存，OFF为正常，NO为异常
        /// </summary>
        public void SbjkGroup(OpcTagModel opcTag, int result)
        {
            var systemDevices = GolbalVariable.SystemDeviceBag.Where(x => x.SignalMark == opcTag.SignalMark && x.DataSource == "OPC").ToList();
            systemDevices.ForEach(x =>
            {
                //设备异常时把异常信息保存到数据库，触发前状态已经是异常的不做记录(连续异常只记录第一次)
                if (result == 1 && x.Status != result)
                {
                    SystemDeviceMonitorModel model = new SystemDeviceMonitorModel()
                    {
                        DeviceId = x.ID.ToString(),
                        DeviceName = x.DeviceName,
                        ExceptionDescription = "设备通讯异常",
                        ExceptionDate = DateTime.Now
                    };
                    //NLoggerHelper.INFO("设备异常(" + x.device_name + ")");
                    bllSystemDeviceMonitor.Add(model);
                }
                //更新设备缓存状态
                x.Status = result;
            });
        }

        /// <summary>
        /// OPC PICS组处理逻辑
        /// </summary>
        public void PicsGroup(OpcTagModel opcTag, int result)
        {

        }

        /// <summary>
        /// 重置拉绳灯
        /// </summary>
        public void ResetSignalLamp()
        {
            //停止所有信号灯闪烁
            Thread thread;
            foreach (var cd in GolbalVariable.SignalLampBlinkThreadDictionary)
            {
                thread = cd.Value;
                if (GolbalVariable.SignalLampBlinkThreadDictionary.TryRemove(cd.Key, out thread))
                {
                    thread.Abort();
                }
            }
            var opcGroup = GolbalVariable.OpcDaGroupList.Where(p => p.Name == "LSD").FirstOrDefault();
            var opcTags = GolbalVariable.OpcTagBag.Where(p => p.GroupId == GolbalVariable.opcGroupList.Where(g => g.GroupName == "LSD").FirstOrDefault().ID).ToList();

            List<string> validate = new List<string>();
            List<object> values = new List<object>();
            foreach (var model in opcTags)
            {
                validate.Add(model.TagName);
                values.Add(0);
            }
            //将数据更新至OPC
            HRESULT[] result1 = GolbalVariable.OpcHelper.WriteValue(opcGroup, validate, values);
            //所有信号灯状态重置
            foreach (var lamp in GolbalVariable.SignalLampDirectionBag)
            {
                lamp.Status = 0;
            }
        }

        readonly object signalLampControlObject = new object();
        /// <summary>
        /// 信号灯状态控制
        /// </summary>
        /// <param name="opcGroup">OPC组</param>
        /// <param name="tagName">OPC tag名称</param>
        /// <param name="status">状态值：0熄灭；1点亮</param>
        private void SignalLampControl(OpcDaGroup opcGroup, string tagName, int status)
        {
            lock (signalLampControlObject)
            {
                //先检查信号灯是否在运行闪烁线程，有则先销毁该线程，然后再下一次操作
                Thread thread;
                if (GolbalVariable.SignalLampBlinkThreadDictionary.TryGetValue(tagName, out thread))
                {
                    if (GolbalVariable.SignalLampBlinkThreadDictionary.TryRemove(tagName, out thread))
                    {
                        thread.Abort();
                    }
                }
                List<string> validate = new List<string>();
                List<object> values = new List<object>();
                validate.Add(tagName);
                values.Add(status);
                //将数据更新至OPC
                HRESULT[] result1 = GolbalVariable.OpcHelper.WriteValue(opcGroup, validate, values);
            }
        }

        /// <summary>
        /// 信号灯闪烁线程
        /// </summary>
        /// <param name="opcGroup">OPC组</param>
        /// <param name="tagName">OPC tag名称</param>
        private void SignalLampBlink(OpcDaGroup opcGroup, string tagName)
        {
            List<string> validate = new List<string>();
            List<object> values_0 = new List<object>();
            List<object> values_1 = new List<object>();
            validate.Add(tagName);
            values_0.Add(0);
            values_1.Add(1);
            while (true)
            {
                GolbalVariable.OpcHelper.WriteValue(opcGroup, validate, values_1);
                Thread.Sleep(500);
                GolbalVariable.OpcHelper.WriteValue(opcGroup, validate, values_0);
                Thread.Sleep(500);
            }
        }
    }
}
